package ch.fhnw.ams.sudoku.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import ch.fhnw.ams.sudoku.Globals;
import ch.fhnw.ams.sudoku.SudokuField;
import ch.fhnw.ams.sudoku.SudokuGrid;

/**
 * The file implementation of the {@link SudokuDAO}.
 * 
 * @author amsler
 *
 */
public class SudokuFileDAO implements SudokuDAO {
	
	private final static String GRID_SEPARATOR = "\t";
	private final static String TAG_SEPARATOR = "=";
	private final static String INFO_TAG = "#";
	private final static String HEADER = "Sudoku SideKick";
	private final static String USER_TAG = "user";
	private final static String DATE_TAG = "date";
	private final static String TIME_PLAYED_TAG = "timePlayed";
	private final static String GRID_TAG = "grid";
	
	private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
	
	private String playerName;
	private Date creationDate;
	private long timePlayed;
	private SudokuGrid sudokuGrid;
	
	
	/**
	 * Creates a new file DAO.
	 */
	public SudokuFileDAO() {
		playerName = Globals.USER_NAME;
		creationDate = new Date();
		timePlayed = 0;
		sudokuGrid = new SudokuGrid();
	}
	
	
	@Override
	public void load(String id) throws SudokuDAOException {
		BufferedReader in = null;
		
		try {
			in = new BufferedReader(new FileReader(id));
			
			String line;
			while ((line = in.readLine()) != null) {
				if (line.startsWith(INFO_TAG)) continue;
				
				String[] split = line.split(TAG_SEPARATOR);
				if (split.length == 2) {
					String tag = split[0].trim();
					String data = split[1].trim();
					readTag(tag, data);
				} if (split.length == 1 && split[0].equals(GRID_TAG)) {
					readGrid(in);
				}
			}
		} catch (IOException e) {
			throw new SudokuDAOException(e);
		} finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (IOException e) {
				throw new SudokuDAOException(e);
			}
		}
	}
	
	private void readTag(String tag, String data) {
		if (tag.equals(USER_TAG)) {
			setPlayerName(data);
		} 
		else if (tag.equals(DATE_TAG)) {
			Date date = new Date();
			try {
				date = DATE_FORMAT.parse(data);
			} catch (ParseException e) {
			}
			setCreationDate(date);
		} 
		else if (tag.equals(TIME_PLAYED_TAG)) {
			long timePlayed = Long.parseLong(data);
			setTimePlayed(timePlayed);
		}
	}
	
	private void readGrid(BufferedReader in) throws IOException, SudokuDAOException {
		
		for (int row = 0; row < SudokuGrid.ROW_SIZE; row++) {
			String line = in.readLine();
			if (line == null) throw new SudokuDAOException("invalid SudokuGrid format");
			
			String[] split = line.split(GRID_SEPARATOR);
			if (split.length != SudokuGrid.COLUMN_SIZE) {
				throw new SudokuDAOException("invalid SudokuGrid format");
			}
			
			for (int column = 0; column < split.length; column++) {
				try {
					int number = Integer.parseInt(split[column]);
					SudokuField field = sudokuGrid.getField(row, column);
					field.setNumber(Math.abs(number));
					if (number > 0) {
						field.setPuzzelNumber(true);
					} else {
						field.setPuzzelNumber(false);
					}
				} catch (NumberFormatException e) {
					throw new SudokuDAOException("wrong SudokuGrid format");
				}
			}
		}
	}

	@Override
	public void save(String id) throws SudokuDAOException {
		BufferedWriter out = null;
		
		try {
			out = new BufferedWriter(new FileWriter(id));
			out.write(INFO_TAG + HEADER);
			out.newLine();
			out.write(USER_TAG + TAG_SEPARATOR + getPlayerName());
			out.newLine();
			out.write(DATE_TAG + TAG_SEPARATOR + DATE_FORMAT.format(getCreationDate()));
			out.newLine();
			out.write(TIME_PLAYED_TAG + TAG_SEPARATOR + getTimePlayed());
			out.newLine();
			writeGrid(out, getSudokuGrid());
		} catch (IOException e) {
			throw new SudokuDAOException(e);
		} finally {
			try {
				if (out != null) {
					out.flush();
					out.close();
				}
			} catch (IOException e) {
				throw new SudokuDAOException(e);
			}
		}
	}
	
	private void writeGrid(BufferedWriter out, SudokuGrid grid) throws IOException {
		out.write(GRID_TAG + TAG_SEPARATOR);
		
		for (int row = 0; row < SudokuGrid.COLUMN_SIZE; row++) {
			out.newLine();
			for (int column = 0; column < SudokuGrid.ROW_SIZE; column++) {
				SudokuField field = grid.getField(row, column);
				int number = field.getNumber();
				if (!field.isPuzzelNumber() && !field.isEmpty()) {
					number *= -1;
				}
				out.write(number + GRID_SEPARATOR);
			}
		}
	}

	@Override
	public String getPlayerName() {
		return playerName;
	}

	@Override
	public void setPlayerName(String playerName) {
		this.playerName = playerName;
	}

	@Override
	public Date getCreationDate() {
		return creationDate;
	}

	@Override
	public void setCreationDate(Date creationDate) {
		this.creationDate = creationDate;
	}

	@Override
	public long getTimePlayed() {
		return timePlayed;
	}

	@Override
	public void setTimePlayed(long timePlayed) {
		this.timePlayed = timePlayed;
	}

	@Override
	public SudokuGrid getSudokuGrid() {
		return sudokuGrid;
	}

	@Override
	public void setSudokuGrid(SudokuGrid sudokuGrid) {
		this.sudokuGrid = sudokuGrid;
	}

}
